@openim/im-composer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-12-29
9
+
10
+ ### Added
11
+
12
+ - **Dual Mode Support**: Plain text and Rich text (Markdown) modes
13
+ - **@Mentions**: Type `@` to search and mention users with customizable provider
14
+ - **Attachments**: Paste or drag files with image preview (plain mode)
15
+ - **Quote Messages**: Reply to messages with quote blocks (plain mode)
16
+ - **Emoji Support**: Full Unicode emoji with optional Twemoji integration
17
+ - **Draft Support**: Save and restore editor state with `getDraft()`/`setDraft()`
18
+ - **Rich Text Features**:
19
+ - Formatting toolbar (bold, italic, strikethrough, code)
20
+ - Headings (H1, H2, H3)
21
+ - Lists (bullet and ordered)
22
+ - Code blocks
23
+ - Block quotes
24
+ - Links with click-to-edit popup
25
+ - Image upload and embedding
26
+ - **i18n Support**: Customizable locale for all text labels
27
+ - **Keyboard Shortcuts**:
28
+ - Configurable send key (Enter / Ctrl+Enter / Cmd+Enter)
29
+ - Markdown shortcuts (e.g., `**bold**`, `# heading`)
30
+ - Link shortcut (Cmd/Ctrl + K)
31
+ - **Ref Methods**: Focus, clear, export, import, insert text, and more
32
+ - **TypeScript**: Full type definitions included
33
+
34
+ ### Dependencies
35
+
36
+ - Built on [Lexical](https://lexical.dev/) editor framework
37
+ - Requires React 18+ or 19+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 im-composer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # @openim/im-composer
2
+
3
+ A React IM composer component with dual mode (plain/rich) supporting @mentions, emoji, attachments, and markdown.
4
+
5
+ Built with [Lexical](https://lexical.dev/) editor framework.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @openim/im-composer
11
+ # or
12
+ pnpm add @openim/im-composer
13
+ ```
14
+
15
+ **Note:** Requires React 18+ or React 19+ as peer dependencies.
16
+
17
+ ## Quick Start
18
+
19
+ ```tsx
20
+ import { IMComposer, IMComposerRef } from '@openim/im-composer';
21
+ import '@openim/im-composer/styles.css';
22
+
23
+ function Chat() {
24
+ const composerRef = useRef<IMComposerRef>(null);
25
+
26
+ return (
27
+ <IMComposer
28
+ ref={composerRef}
29
+ mode="plain"
30
+ placeholder="Type a message..."
31
+ onSend={(payload) => {
32
+ console.log('Sent:', payload);
33
+ }}
34
+ mentionProvider={async (query) => {
35
+ // Return list of users matching query
36
+ return [{ userId: '1', display: 'John', avatarUrl: '...' }];
37
+ }}
38
+ />
39
+ );
40
+ }
41
+ ```
42
+
43
+ ## Features
44
+
45
+ ### Plain Text Mode
46
+ - **@Mentions** - Type `@` to search and mention users
47
+ - **Attachments** - Paste or drag files with preview
48
+ - **Quote Messages** - Reply to messages with quote blocks
49
+ - **Emoji** - Full Unicode emoji support
50
+ - **Draft Support** - Save and restore editor state
51
+
52
+ ### Rich Text Mode (Markdown)
53
+ - **Formatting Toolbar** - Bold, italic, strikethrough, code
54
+ - **Headings** - H1, H2, H3
55
+ - **Lists** - Bullet and ordered lists
56
+ - **Code Blocks** - Inline code and code blocks
57
+ - **Block Quotes** - Quote formatting
58
+ - **Links** - Insert and edit links with popup
59
+ - **Images** - Upload and embed images
60
+
61
+ ## Props
62
+
63
+ ### Basic Props
64
+
65
+ | Prop | Type | Default | Description |
66
+ |------|------|---------|-------------|
67
+ | `mode` | `'plain' \| 'rich'` | - | Controlled mode |
68
+ | `defaultMode` | `'plain' \| 'rich'` | `'plain'` | Default mode (uncontrolled) |
69
+ | `onSend` | `(payload) => void` | - | Callback when user sends message |
70
+ | `placeholder` | `string \| { plain?: string; rich?: string }` | - | Placeholder text |
71
+ | `disabled` | `boolean` | `false` | Disable the editor |
72
+ | `className` | `string` | - | Custom class name |
73
+ | `onChange` | `() => void` | - | Callback on content change |
74
+
75
+ ### Mention Options (Plain Mode)
76
+
77
+ | Prop | Type | Default | Description |
78
+ |------|------|---------|-------------|
79
+ | `enableMention` | `boolean` | `true` | Enable @mention feature |
80
+ | `mentionProvider` | `(query: string) => Promise<Member[]>` | - | Async function to search members |
81
+ | `maxMentions` | `number` | - | Maximum mentions allowed |
82
+ | `renderMentionItem` | `(props) => ReactNode` | - | Custom render for mention dropdown items |
83
+
84
+ ### Attachment Options (Plain Mode)
85
+
86
+ | Prop | Type | Default | Description |
87
+ |------|------|---------|-------------|
88
+ | `enableAttachments` | `boolean` | `true` | Enable file attachments |
89
+ | `showAttachmentPreview` | `boolean` | `true` | Show built-in attachment preview bar |
90
+ | `attachmentPreviewPlacement` | `'top' \| 'bottom'` | `'bottom'` | Position of preview bar |
91
+ | `maxAttachments` | `number` | `10` | Maximum attachments allowed |
92
+ | `maxFileSize` | `number` | - | Maximum file size in bytes |
93
+ | `allowedMimeTypes` | `string[]` | - | Allowed MIME types |
94
+ | `onAttachmentLimitExceeded` | `(reason, file) => void` | - | Callback when limit exceeded |
95
+ | `onFilesChange` | `(attachments) => void` | - | Callback when attachments change |
96
+
97
+ ### Rich Mode Options
98
+
99
+ | Prop | Type | Default | Description |
100
+ |------|------|---------|-------------|
101
+ | `uploadImage` | `(file: File) => Promise<{ url: string; alt?: string }>` | - | Image upload function |
102
+ | `markdownOptions.enabledSyntax` | `MarkdownSyntaxOptions` | All enabled | Enable/disable specific markdown features |
103
+
104
+ ### Keymap Options
105
+
106
+ | Prop | Type | Default | Description |
107
+ |------|------|---------|-------------|
108
+ | `keymap.send` | `'enter' \| 'ctrlEnter' \| 'cmdEnter'` | `'enter'` | Key to send message |
109
+
110
+ ### Other Props
111
+
112
+ | Prop | Type | Description |
113
+ |------|------|-------------|
114
+ | `onContextMenu` | `MouseEventHandler` | Right-click handler |
115
+ | `onQuoteRemoved` | `() => void` | Callback when quote is removed |
116
+ | `locale` | `IMComposerLocale` | i18n configuration |
117
+
118
+ ## Ref Methods
119
+
120
+ Access via `useRef<IMComposerRef>()`:
121
+
122
+ ```tsx
123
+ const composerRef = useRef<IMComposerRef>(null);
124
+
125
+ // Focus the editor
126
+ composerRef.current?.focus();
127
+
128
+ // Clear content
129
+ composerRef.current?.clear();
130
+
131
+ // Export payload without sending
132
+ const payload = composerRef.current?.exportPayload();
133
+
134
+ // Insert text at cursor
135
+ composerRef.current?.insertText('Hello 👋');
136
+ ```
137
+
138
+ ### All Methods
139
+
140
+ | Method | Description |
141
+ |--------|-------------|
142
+ | `focus()` | Focus the editor |
143
+ | `clear()` | Clear editor content and attachments |
144
+ | `exportPayload()` | Export current payload without sending |
145
+ | `importMarkdown(markdown)` | Import markdown content (rich mode) |
146
+ | `getAttachments()` | Get current attachments (plain mode) |
147
+ | `setAttachments(attachments)` | Set attachments (plain mode) |
148
+ | `addFiles(files)` | Add files to attachments (plain mode) |
149
+ | `removeAttachment(id)` | Remove attachment by ID (plain mode) |
150
+ | `clearAttachments()` | Clear all attachments (plain mode) |
151
+ | `insertQuote(title, content)` | Insert a quote message (plain mode) |
152
+ | `insertMention(userId, display)` | Insert a mention (plain mode) |
153
+ | `getDraft()` | Get draft state for saving |
154
+ | `setDraft(draft)` | Restore draft state |
155
+ | `setText(text, mentions?)` | Set editor content from plain text |
156
+ | `insertText(text)` | Insert text at cursor position |
157
+
158
+ ## Payload Types
159
+
160
+ ### Plain Text Payload
161
+
162
+ ```typescript
163
+ type PlainMessagePayload = {
164
+ type: 'text';
165
+ plainText: string; // Text with @mentions as @{display}
166
+ mentions: MentionInfo[]; // Position info for each mention
167
+ attachments: Attachment[];
168
+ quote?: QuoteInfo;
169
+ };
170
+ ```
171
+
172
+ ### Markdown Payload
173
+
174
+ ```typescript
175
+ type MarkdownMessagePayload = {
176
+ type: 'markdown';
177
+ markdown: string; // Full markdown content
178
+ };
179
+ ```
180
+
181
+ ## i18n / Localization
182
+
183
+ Customize text labels with the `locale` prop:
184
+
185
+ ```tsx
186
+ <IMComposer
187
+ locale={{
188
+ linkDialog: {
189
+ textLabel: '文本',
190
+ urlLabel: '链接',
191
+ cancelButton: '取消',
192
+ insertButton: '插入',
193
+ saveButton: '保存',
194
+ },
195
+ toolbar: {
196
+ bold: '粗体',
197
+ italic: '斜体',
198
+ link: '插入链接',
199
+ image: '插入图片',
200
+ // ... more toolbar labels
201
+ },
202
+ }}
203
+ />
204
+ ```
205
+
206
+ ## Draft Support
207
+
208
+ Save and restore editor state for drafts:
209
+
210
+ ```tsx
211
+ // Save draft
212
+ const draft = composerRef.current?.getDraft();
213
+ localStorage.setItem('draft', JSON.stringify(draft));
214
+
215
+ // Restore draft
216
+ const saved = localStorage.getItem('draft');
217
+ if (saved) {
218
+ composerRef.current?.setDraft(JSON.parse(saved));
219
+ }
220
+ ```
221
+
222
+ ## Markdown Syntax Control
223
+
224
+ Enable/disable specific markdown features:
225
+
226
+ ```tsx
227
+ <IMComposer
228
+ mode="rich"
229
+ markdownOptions={{
230
+ enabledSyntax: {
231
+ heading: true,
232
+ bold: true,
233
+ italic: true,
234
+ strike: true,
235
+ codeInline: true,
236
+ codeBlock: true,
237
+ quote: true,
238
+ list: true,
239
+ link: true,
240
+ image: true, // Requires uploadImage prop
241
+ },
242
+ }}
243
+ />
244
+ ```
245
+
246
+ ## Custom Mention Item Rendering
247
+
248
+ ```tsx
249
+ <IMComposer
250
+ mentionProvider={searchUsers}
251
+ renderMentionItem={({ member, isSelected }) => (
252
+ <div className={`mention-item ${isSelected ? 'selected' : ''}`}>
253
+ <img src={member.avatarUrl} alt="" />
254
+ <span>{member.display}</span>
255
+ </div>
256
+ )}
257
+ />
258
+ ```
259
+
260
+ ## Styling
261
+
262
+ Import the default styles:
263
+
264
+ ```tsx
265
+ import 'im-composer/styles.css';
266
+ ```
267
+
268
+ Customize with CSS variables or override the classes:
269
+
270
+ ```css
271
+ .im-composer {
272
+ --im-composer-border-color: #e0e0e0;
273
+ --im-composer-focus-color: #1890ff;
274
+ }
275
+ ```
276
+
277
+ ## Exported Types
278
+
279
+ ```tsx
280
+ import type {
281
+ IMComposerProps,
282
+ IMComposerRef,
283
+ IMComposerLocale,
284
+ PlainMessagePayload,
285
+ MarkdownMessagePayload,
286
+ MessagePayload,
287
+ Member,
288
+ MentionInfo,
289
+ Attachment,
290
+ QuoteInfo,
291
+ ComposerMode,
292
+ UploadImageFn,
293
+ UploadImageResult,
294
+ MarkdownSyntaxOptions,
295
+ SendKey,
296
+ } from 'im-composer';
297
+ ```
298
+
299
+ ## License
300
+
301
+ This package is licensed under the MIT License.
302
+
303
+ ## Note on Emoji Rendering
304
+
305
+ This package uses CSS font-family declarations for emoji rendering. For consistent emoji display across platforms:
306
+
307
+ 1. Include an emoji font (like Twemoji) in your project
308
+ 2. If using Twemoji graphics, ensure proper attribution per CC BY 4.0 license